"""
API场景组合测试工具 - 执行服务
"""
import json
import requests
import threading
import time
from datetime import datetime
from app import db
from app.models.scene import Scene
from app.models.scene_step import SceneStep
from app.models.api_info import ApiInfo
from app.models.environment import Environment
from app.models.execution_record import ExecutionRecord
from app.models.step_execution_record import StepExecutionRecord
from app.models.auth_cache import AuthCache
from app.services.script_engine import ScriptEngine
from app.services.auth_service import AuthService
from app.services.template_service import TemplateService
from app.utils.variable_processor import process_variables

class ExecutionService:
    """场景执行服务"""
    
    def __init__(self):
        """初始化"""
        self.running_executions = {}  # 存储正在运行的执行ID和线程
        self.script_engine = ScriptEngine()
        self.auth_service = AuthService()
        self.template_service = TemplateService()
    
    def execute_scene_async(self, execution_id, scene_id, environment_id, template_data_id=None, template_env_id=None):
        """异步执行场景"""
        # 创建执行线程
        thread = threading.Thread(
            target=self.execute_scene,
            args=(execution_id, scene_id, environment_id, template_data_id, template_env_id)
        )
        
        # 存储线程
        self.running_executions[execution_id] = thread
        
        # 启动线程
        thread.start()
    
    def execute_scene(self, execution_id, scene_id, environment_id, template_data_id=None, template_env_id=None):
        """执行场景"""
        try:
            # 获取场景和环境
            scene = Scene.query.get(scene_id)
            environment = Environment.query.get(environment_id)
            
            if not scene or not environment:
                self._update_execution_status(execution_id, 'FAILED', {'error': '场景或环境不存在'})
                return
            
            # 获取场景步骤
            steps = SceneStep.query.filter_by(scene_id=scene_id).order_by(SceneStep.order).all()
            
            # 初始化上下文
            context = {
                'execution_id': execution_id,
                'scene_id': scene_id,
                'environment_id': environment_id,
                'environment': json.loads(environment.config) if environment.config else {},
                'auth': {},
                'variables': {},
                'results': {}
            }
            
            # 获取模板数据
            if template_data_id and template_env_id:
                template_env = Environment.query.get(template_env_id)
                if template_env:
                    template_data = self.template_service.get_template_data(
                        template_data_id, 
                        json.loads(template_env.config) if template_env.config else {}
                    )
                    if template_data:
                        context['template_data'] = template_data
            
            # 执行场景脚本（如果有）
            if scene.script:
                try:
                    script_result = self.script_engine.execute_script(scene.script, context)
                    context.update(script_result)
                except Exception as e:
                    self._update_execution_status(execution_id, 'FAILED', {'error': f'场景脚本执行失败: {str(e)}'})
                    return
            
            # 执行步骤
            for step in steps:
                # 检查是否取消
                if self._is_execution_cancelled(execution_id):
                    return
                
                # 执行步骤
                step_result = self._execute_step(step, context)
                
                # 如果步骤执行失败，整个场景执行失败
                if step_result.get('status') == 'FAILED':
                    self._update_execution_status(execution_id, 'FAILED', {'error': step_result.get('error')})
                    return
                
                # 将步骤结果添加到上下文
                context['results'][step.id] = step_result.get('response')
                
                # 如果步骤有返回变量，添加到上下文
                if 'variables' in step_result:
                    context['variables'].update(step_result['variables'])
            
            # 更新执行状态为成功
            self._update_execution_status(execution_id, 'SUCCESS', {'message': '场景执行成功'})
            
        except Exception as e:
            # 更新执行状态为失败
            self._update_execution_status(execution_id, 'FAILED', {'error': str(e)})
        finally:
            # 从运行列表中移除
            if execution_id in self.running_executions:
                del self.running_executions[execution_id]
    
    def _execute_step(self, step, context):
        """执行单个步骤"""
        # 创建步骤执行记录
        step_record = StepExecutionRecord(
            execution_id=context['execution_id'],
            step_id=step.id,
            step_name=step.name,
            step_type=step.step_type,
            status='RUNNING'
        )
        db.session.add(step_record)
        db.session.commit()
        
        try:
            result = None
            
            # 根据步骤类型执行
            if step.step_type == 'API':
                result = self._execute_api_step(step, context)
            elif step.step_type == 'SCENE':
                result = self._execute_scene_step(step, context)
            elif step.step_type == 'SCRIPT':
                result = self._execute_script_step(step, context)
            else:
                raise ValueError(f'不支持的步骤类型: {step.step_type}')
            
            # 更新步骤执行记录
            step_record.status = result.get('status', 'SUCCESS')
            step_record.end_time = datetime.now()
            step_record.request = json.dumps(result.get('request', {}))
            step_record.response = json.dumps(result.get('response', {}))
            db.session.commit()
            
            return result
        except Exception as e:
            # 更新步骤执行记录为失败
            step_record.status = 'FAILED'
            step_record.end_time = datetime.now()
            step_record.error = str(e)
            db.session.commit()
            
            return {'status': 'FAILED', 'error': str(e)}
    
    def _execute_api_step(self, step, context):
        """执行API步骤"""
        # 获取API信息
        api = ApiInfo.query.get(step.api_id)
        if not api:
            return {'status': 'FAILED', 'error': f'API不存在: {step.api_id}'}
        
        # 获取环境信息
        environment = Environment.query.get(context['environment_id'])
        env_config = json.loads(environment.config) if environment.config else {}
        
        # 处理URL
        base_url = environment.base_url or env_config.get('base_url', '')
        url = api.url
        if not url.startswith(('http://', 'https://')):
            url = f"{base_url.rstrip('/')}/{url.lstrip('/')}"
        
        # 处理参数
        params = json.loads(step.params) if step.params else {}
        
        # 处理请求头
        headers = json.loads(api.headers) if api.headers else {}
        
        # 添加认证信息
        auth_headers = self._get_auth_headers(context)
        headers.update(auth_headers)
        
        # 处理请求体
        body = api.body_template or '{}'
        
        # 处理变量替换
        url = process_variables(url, context)
        body = process_variables(body, context)
        
        # 处理JSON字符串转换为字典
        try:
            body_dict = json.loads(body)
        except:
            body_dict = {}
        
        # 记录请求信息
        request_info = {
            'url': url,
            'method': api.method,
            'headers': headers,
            'body': body_dict
        }
        
        # 发送请求
        try:
            response = requests.request(
                method=api.method,
                url=url,
                headers=headers,
                json=body_dict if api.method.upper() in ['POST', 'PUT', 'PATCH'] else None,
                params=body_dict if api.method.upper() == 'GET' else None,
                timeout=30
            )
            
            # 解析响应
            try:
                response_data = response.json()
            except:
                response_data = {'text': response.text}
            
            # 处理脚本（如果有）
            variables = {}
            if step.script_content:
                script_context = {
                    'response': response_data,
                    'request': request_info,
                    'context': context
                }
                
                script_result = self.script_engine.execute_script(step.script_content, script_context)
                variables = script_result.get('variables', {})
            
            return {
                'status': 'SUCCESS',
                'request': request_info,
                'response': response_data,
                'variables': variables
            }
        except Exception as e:
            return {
                'status': 'FAILED',
                'request': request_info,
                'error': str(e)
            }
    
    def _execute_scene_step(self, step, context):
        """执行场景步骤（引用其他场景）"""
        # 获取引用的场景
        referenced_scene = Scene.query.get(step.referenced_scene_id)
        if not referenced_scene:
            return {'status': 'FAILED', 'error': f'引用的场景不存在: {step.referenced_scene_id}'}
        
        # 获取场景步骤
        steps = SceneStep.query.filter_by(scene_id=step.referenced_scene_id).order_by(SceneStep.order).all()
        
        # 创建子上下文
        sub_context = context.copy()
        
        # 执行场景脚本（如果有）
        if referenced_scene.script:
            try:
                script_result = self.script_engine.execute_script(referenced_scene.script, sub_context)
                sub_context.update(script_result)
            except Exception as e:
                return {'status': 'FAILED', 'error': f'引用场景脚本执行失败: {str(e)}'}
        
        # 执行步骤
        step_results = []
        for sub_step in steps:
            # 执行步骤
            step_result = self._execute_step(sub_step, sub_context)
            step_results.append(step_result)
            
            # 如果步骤执行失败，整个场景执行失败
            if step_result.get('status') == 'FAILED':
                return {'status': 'FAILED', 'error': step_result.get('error')}
            
            # 将步骤结果添加到上下文
            sub_context['results'][sub_step.id] = step_result.get('response')
            
            # 如果步骤有返回变量，添加到上下文
            if 'variables' in step_result:
                sub_context['variables'].update(step_result['variables'])
        
        # 更新主上下文的变量
        context['variables'].update(sub_context['variables'])
        
        return {
            'status': 'SUCCESS',
            'response': {
                'scene_name': referenced_scene.name,
                'steps': len(step_results),
                'results': step_results
            },
            'variables': sub_context['variables']
        }
    
    def _execute_script_step(self, step, context):
        """执行脚本步骤"""
        if not step.script_content:
            return {'status': 'FAILED', 'error': '脚本内容为空'}
        
        try:
            # 执行脚本
            script_result = self.script_engine.execute_script(step.script_content, context)
            
            return {
                'status': 'SUCCESS',
                'response': script_result.get('result', {}),
                'variables': script_result.get('variables', {})
            }
        except Exception as e:
            return {'status': 'FAILED', 'error': f'脚本执行失败: {str(e)}'}
    
    def _get_auth_headers(self, context):
        """获取认证头信息"""
        # 检查上下文中是否已有认证信息
        if context.get('auth'):
            return context['auth'].get('headers', {})
        
        # 获取环境ID
        environment_id = context['environment_id']
        
        # 获取认证缓存
        auth_cache = AuthCache.query.filter_by(
            environment_id=environment_id, 
            is_valid=True
        ).filter(
            (AuthCache.expires_at.is_(None)) | (AuthCache.expires_at > datetime.now())
        ).order_by(AuthCache.created_at.desc()).first()
        
        # 如果有有效的认证缓存，使用它
        if auth_cache:
            auth_data = json.loads(auth_cache.auth_data) if auth_cache.auth_data else {}
            context['auth'] = auth_data
            return auth_data.get('headers', {})
        
        # 否则，执行认证流程
        environment = Environment.query.get(environment_id)
        auth_config = json.loads(environment.auth_config) if environment.auth_config else {}
        
        # 如果没有认证配置，返回空
        if not auth_config:
            return {}
        
        # 执行认证
        auth_result = self.auth_service.authenticate(environment_id, auth_config)
        
        # 更新上下文
        context['auth'] = auth_result
        
        return auth_result.get('headers', {})
    
    def _update_execution_status(self, execution_id, status, result=None):
        """更新执行状态"""
        execution = ExecutionRecord.query.get(execution_id)
        if execution:
            execution.status = status
            execution.end_time = datetime.now()
            if result:
                execution.result = json.dumps(result)
            db.session.commit()
    
    def _is_execution_cancelled(self, execution_id):
        """检查执行是否被取消"""
        execution = ExecutionRecord.query.get(execution_id)
        return execution and execution.status == 'CANCELLED'
    
    def cancel_execution(self, execution_id):
        """取消执行"""
        # 更新执行状态
        self._update_execution_status(execution_id, 'CANCELLED', {'message': '执行被用户取消'})
        
        # 从运行列表中移除
        if execution_id in self.running_executions:
            # 注意：Python线程无法强制终止，只能通过标志位来控制
            del self.running_executions[execution_id]